iT邦幫忙

2024 iThome 鐵人賽

DAY 25
0

昨天我設計好了列表元件,
但發現列表缺少新增、修改、刪除的功能。
因此,我決定在原本的元件上做一些調整。
不過這邊發現有個BUG
昨天寫的程式輸入會有問題
後來經過檢查以後發現是form變更會影響getGroupList的結果
所以這邊調整成getGroupList只在初始化的時候產生一次

// form-input.component.ts
@Input() pageSetting!: PageSetting
fieldObj!: object;
groupList!: any[]; // 新增變數控制
innerForm = this.fb.group({});
constructor(private fb: FormBuilder,
    public shareService: ShareService,
    private validatorService: ValidatorService
) { }
private destroy$ = new Subject<void>(); // 用來發出結束通知

ngOnChanges(): void
{
    this.pageSetting.form = this.fb.group({});
    this.innerForm = this.fb.group({});
    this.pageSetting.fieldSettings.forEach(setting =>
    {
        let newControl = this.fb.control(setting.defaultValue) as FormControl;

        if (setting.validator && setting.validator?.length > 0)
        {
            if (!!setting.validator.find(v => v == 'required')) setting.required = true;
            newControl.addValidators(this.validatorService.getValidators(setting.validator))
        }

        if (setting.groupType == 'list' && !!setting.groupName)
        {
            if (!this.innerForm.contains(setting.groupName))
            {
                let newFormArray = new FormArray([])
                this.innerForm.addControl(setting.groupName, newFormArray);
                this.pageSetting.form?.addControl(setting.groupName, newFormArray);
            }
            return
        }
        (this.pageSetting.form as FormGroup).addControl(setting.name, newControl);
        if (!setting.groupName)
        {
            this.innerForm.addControl(setting.name, newControl);
        } else
        {
            if (!this.innerForm.contains(setting.groupName))
            {
                this.innerForm.addControl(setting.groupName, new FormGroup({}));
            }
            (this.innerForm.get(setting.groupName) as FormGroup).addControl(setting.name!, this.pageSetting.form?.get(setting.name));

            (this.pageSetting.form?.get(setting.name) as FormGroup)
                .valueChanges.pipe(takeUntil(this.destroy$)).subscribe(v =>
                {
                    this.pageSetting.form?.updateValueAndValidity();
                })
        }

    });
    this.getGroupList(this.innerForm); // 調整
}
// form-input.component.html
    <div
      *ngFor="let fieldObj of groupList"
      [ngClass]="{
        'col-6': shareService.typeof(fieldObj) === 'string',
        'col-12': shareService.typeof(fieldObj) !== 'string'
      }"
    >

上面原本使用getGroupList的部分改成採用groupList

然後,原本的輸入框會改成純顯示,
並在最下方新增一排輸入欄位,用來添加新的資料。
當使用者新增資料時,資料會被加到 inputFormArray 裡面。
此外,原本資料的旁邊會增加一個「修改」按鈕,點擊後,
該行的資料會從顯示狀態變成可輸入狀態,
且輸入模板會複製原本該行的資料進行編輯。

// list.component.html
<table [formGroup]="inputForm">
  <thead>
    <tr>
      <th *ngFor="let title of titleArr">
        {{ title }}
      </th>
    </tr>
  </thead>
  <tbody formArrayName="{{ listKey }}">
    <tr
      *ngFor="let row of inputFormArray.controls; let i = index"
      [formGroupName]="i"
    >
      <ng-container
        *ngTemplateOutlet="
          editRowNum != i ? displayRow : editRow;
          context: editRowNum != i
            ? { inTeplateForm: row, index: i }
            : { inTeplateForm: subFormGroup, index: i }
        "
      ></ng-container>
    </tr>
    <tr>
      <ng-container
        *ngTemplateOutlet="
          editRowNum < inputFormArray.controls.length ? emptyRow : addRow;
          context: { inTeplateForm: subFormGroup }
        "
      >
      </ng-container>
    </tr>
  </tbody>
</table>

<ng-template #emptyRow></ng-template>
<ng-template #displayRow let-fg="inTeplateForm" let-i="index">
  <td *ngFor="let key of Object.keys(fg.value)">
    {{ fg.get(key)?.value }}
  </td>
  <button (click)="edit(i)">編輯</button>
  <button (click)="delete(i)">刪除</button>
</ng-template>

<ng-template #editRow let-fg="inTeplateForm" let-i="index">
  <ng-container *ngIf="fg">
    <td *ngFor="let key of Object.keys(fg?.value)">
      <app-field
        [fieldSetting]="this.shareService.getSetting(this.fieldSettings, key)"
        [control]="shareService.getControl(fg, key)"
      ></app-field>
    </td>
    <td>
      <button (click)="save(i)">儲存</button>
      <button (click)="cancel()">取消</button>
    </td>
  </ng-container>
</ng-template>
<ng-template #addRow let-fg="inTeplateForm">
  <ng-container *ngIf="fg">
    <td *ngFor="let key of Object.keys(fg?.value)">
      <app-field
        [fieldSetting]="this.shareService.getSetting(this.fieldSettings, key)"
        [control]="shareService.getControl(fg, key)"
      ></app-field>
    </td>
    <td>
      <button (click)="add()">新增</button>
    </td>
  </ng-container>
</ng-template>
// list.component.ts
    add()
    {
        this.inputFormArray.push(this.fb.group(this.subFormGroup.value));
        this.editRowNum = this.inputFormArray.value.length;
    }
    delete(index: number)
    {
        this.inputFormArray.removeAt(index);
    }
    cancel()
    {
        this.subFormGroup.reset();
        this.editRowNum = this.inputFormArray.value.length;
    }
    edit(index: number)
    {
        this.editRowNum = index;
        this.subFormGroup.patchValue(this.inputFormArray.controls[index].value);
    }
    save(index: number)
    {
        this.inputFormArray.controls[index].patchValue(this.subFormGroup.value);
        this.subFormGroup.reset();
        this.editRowNum = this.inputFormArray.value.length;
    }

新增和修改功能可以共用同一個模板,
這個模板可以通過 subFormGroup 的 form 來進行處理。
除了編輯功能,還會提供「刪除」按鈕,
讓使用者可以點擊後刪除該筆資料。

雖然我原本想做一個簡單的新增、修改、刪除功能,
但這個模板的結構似乎變得有點複雜了。
因此,我也有另一個想法,是否可以改用彈窗來處理這些操作。
使用彈窗的話,模板規則就不用那麼複雜,或許會更簡單易行。
我們可以在明天再來討論和決定具體的實現方式。
今日程式:day25


上一篇
第24天 特殊元件 列表
下一篇
第26天 自製彈窗
系列文
簡單的事 最困難-Angular動態Form元件30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言